/*
 * TensorDense.h
 *
 *  Created on: Sep 18, 2011
 *      Author: phopon
 */
#pragma once
#ifndef TENSORDENSE_H_
#define TENSORDENSE_H_

#include <iterator>
#include <functional>
#include <type_traits>

#include <istream>
#include <sstream>

#include <cstring>

#include "TensorBase.h"
#include "TensorIterator.h"
#include "TensorMathElementwise.h"

namespace cmpMat
{
    
template <typename _elementType>
class _TensorDenseIteratorFunctions : public __iterator_functions<_elementType>
{
protected:
    CmpMatPtr _stride;
    typedef __iterator_functions<_elementType> _base;
public:
    typedef typename _base::pointer pointer;
    typedef typename _base::difference_type difference_type;

    _TensorDenseIteratorFunctions() : _stride(1)
    {}
    explicit 
    _TensorDenseIteratorFunctions(CmpMatPtr stride) : _stride(stride)
    {}

    virtual void add(const _elementType*& _m, const difference_type& __n) const
    { _m += __n * _stride; }
    virtual void sub(const _elementType*& _m, const difference_type& __n) const
    { _m -= __n * _stride; }
protected:
    virtual bool deepequal(const __iterator_functions& other) const
    {
        try
        {
            const _TensorDenseIteratorFunctions* otherp = dynamic_cast<const _TensorDenseIteratorFunctions*>(&other);
            return true;//(otherp->_stride == this->_stride); if here this is always true, but in other types, check this
        } catch (const std::bad_cast&) // was not same type of iterator
        {
            return false;
        }
    }

    virtual CmpMatPtr gethash() const { return _stride; }
};

template <typename _elementType>
class TensorDense : public TensorBase<_elementType>
{
public:
    typedef TensorBase<_elementType> _base;
    typedef typename _base::_dimension_type _dimension_type;
    typedef typename _base::_const_dimension_iterator _const_dimension_iterator;
    typedef typename _base::_dimension_iterator _dimension_iterator;
    typedef typename _base::Iterator Iterator;
    typedef typename _base::const_Iterator const_Iterator;

    typedef std::vector<_elementType> _storage_array_type;
    typedef typename _storage_array_type::const_iterator _const_storage_iterator;
    typedef typename _storage_array_type::iterator _storage_iterator;

private:
    // Internal Memory
    _storage_array_type _storage_array;
    _TensorDenseIteratorFunctions<typename std::remove_const<_elementType>::type> _iter_funcs;
protected:

    // set size
    virtual void SetSize();
    virtual void SetSize(const CmpMatPtr& size1);
    virtual void SetSize(const CmpMatPtr& size1, const CmpMatPtr& size2);
    virtual void SetSize(const CmpMatPtr& size1, const CmpMatPtr& size2, const CmpMatPtr& size3);
    virtual void SetSize(const CmpMatPtr& size1, const CmpMatPtr& size2, const CmpMatPtr& size3, const CmpMatPtr& size4);
    // allows arbitrary rank
    virtual void SetSize(const _dimension_type& _dims);
    // end set size
    
    // method for constructing copy from base class
    virtual TensorDense* clone() const;
    
public:
    TensorDense();
    explicit
    TensorDense(const CmpMatPtr& size1);
    TensorDense(const CmpMatPtr& size1, const CmpMatPtr& size2);
    TensorDense(const CmpMatPtr& size1, const CmpMatPtr& size2, const CmpMatPtr& size3);
    TensorDense(const CmpMatPtr& size1, const CmpMatPtr& size2, const CmpMatPtr& size3, const CmpMatPtr& size4);
    // allows arbitrary rank
    TensorDense(const _dimension_type& _dims);

    // copy constructors
    TensorDense(const TensorDense& other);
    TensorDense(TensorDense&& other);
    static TensorDense copyFrom(const TensorDense& other);
    static TensorDense copyFrom(const TensorDense&& other);
    static TensorDense copyFrom(const TensorBase& other);
    // end copy

    virtual ~TensorDense()
    { }

    // load from strings
    static TensorDense loadFromMAT(const std::string& in);
    // load from files/strings
    // sets current tensor from CSV string returns unused portion
    static TensorDense loadFromMAT(std::istream& in);
    // print tensor to string
    virtual std::string toStringFormat(const std::string& format) const;

public:
    // standard operators
    TensorDense& operator=(const TensorDense& other)
    {
        if (&other != this)
        {
            this->_base::operator =(other);
            _storage_array = other._storage_array;
        }
        return *this;
    }
    TensorDense& operator=(TensorDense&& other)
    {
        if (&other != this)
        {
            this->_base::operator =(std::move(other));
            _storage_array = std::move(other._storage_array);
        }
        return *this;
    }
    // end standard ops

    // Math operations
#define UNARYFUNCDECLARE(_name) \
    static TensorDense Elem##_name##(const TensorDense& a)\
    { return TensorMathElementwise::##_name##<cmpMat::TensorDense>(a); }\
    TensorDense Elem##_name##() const\
    { return TensorMathElementwise::##_name##<cmpMat::TensorDense>(*this); }\
    TensorDense& Elem##_name##To()\
    { return TensorMathElementwise::##_name##To<cmpMat::TensorDense>(*this); }
    
    UNARYFUNCDECLARE(Pow2);
    UNARYFUNCDECLARE(Sin);
    UNARYFUNCDECLARE(Cos);
    UNARYFUNCDECLARE(Tan);
    UNARYFUNCDECLARE(Abs);
    UNARYFUNCDECLARE(lAbs);
    UNARYFUNCDECLARE(llAbs);
    UNARYFUNCDECLARE(fAbs);
    UNARYFUNCDECLARE(Acos);
    UNARYFUNCDECLARE(Asin);
    UNARYFUNCDECLARE(Atan);
    UNARYFUNCDECLARE(Cosh);
    UNARYFUNCDECLARE(Exp);
    UNARYFUNCDECLARE(Log);
    UNARYFUNCDECLARE(Log10);
    UNARYFUNCDECLARE(Sinh);
    UNARYFUNCDECLARE(Tanh);
    UNARYFUNCDECLARE(Sqrt);

#define BINARYFUNCDECLARE(_name) \
    template <typename _T1, typename _T2> static TensorDense Elem##_name##(const _T1& a, const _T2& b)\
    { return TensorMathElementwise::##_name##<cmpMat::TensorDense, _elementType>(a, b); }\
    template <typename _T1> TensorDense Elem##_name##(const _T1& other) const\
    { return TensorMathElementwise::##_name##<cmpMat::TensorDense, _elementType>(*this, other); }\
    template <typename _T1> TensorDense& Elem##_name##To(const _T1& other)\
    { return TensorMathElementwise::##_name##To<cmpMat::TensorDense, _elementType>(*this, other); }

    BINARYFUNCDECLARE(Add);
    BINARYFUNCDECLARE(Subtract);
    BINARYFUNCDECLARE(Multiply);
    BINARYFUNCDECLARE(Divide);
    // end Math operations

    // TensorBase Functions
    virtual _elementType& operator[](const CmpMatPtr& dim1)
    { return _storage_array[dim1]; }
    virtual Iterator begin()
    { return Iterator(&front(), _iter_funcs); }
    virtual Iterator end()
    { return Iterator(&back() + 1, _iter_funcs); }
    // End TensorBase Functions
};

} /* namespace cmpMat */


#include "TensorDenseString.h"
#include "TensorDenseConstructors.h"

#endif /* TENSORDENSE_H_ */
